﻿using System;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Algorithm;

namespace YJKGridTopologyCal.Geometries
{
	public class OgcCompliantGeometryFactory : GeometryFactory
	{
		public OgcCompliantGeometryFactory()
		{
		}

		public OgcCompliantGeometryFactory(ICoordinateSequenceFactory factory) : base(factory)
		{
		}

		public OgcCompliantGeometryFactory(IPrecisionModel pm) : base(pm)
		{
		}

		public OgcCompliantGeometryFactory(IPrecisionModel pm, int srid) : base(pm, srid)
		{
		}

		public OgcCompliantGeometryFactory(IPrecisionModel pm, int srid, ICoordinateSequenceFactory factory) : base(pm, srid, factory)
		{
		}

		private static ILinearRing ReverseRing(ILinearRing ring)
		{
			return (ILinearRing)ring.Reverse();
		}

		private ILinearRing CreateLinearRing(Coordinate[] coordinates, bool ccw)
		{
			if (coordinates != null && CGAlgorithms.IsCCW(coordinates) != ccw)
			{
				Array.Reverse(coordinates);
			}
			return base.CreateLinearRing(coordinates);
		}

		private ILinearRing CreateLinearRing(ICoordinateSequence coordinates, bool ccw)
		{
			if (coordinates != null && CGAlgorithms.IsCCW(coordinates) != ccw)
			{
				coordinates = coordinates.Reversed();
			}
			return base.CreateLinearRing(coordinates);
		}

		public override IGeometry ToGeometry(Envelope envelope)
		{
			if (envelope.IsNull)
			{
                return base.CreatePoint((ICoordinateSequence)null);
			}
			if (envelope.MinX == envelope.MaxX && envelope.MinY == envelope.MaxY)
			{
				return base.CreatePoint(new Coordinate(envelope.MinX, envelope.MinY));
			}
			if (envelope.MinX == envelope.MaxX || envelope.MinY == envelope.MaxY)
			{
				return base.CreateLineString(new Coordinate[]
				{
					new Coordinate(envelope.MinX, envelope.MinY),
					new Coordinate(envelope.MaxX, envelope.MaxY)
				});
			}
			ILinearRing shell = base.CreateLinearRing(new Coordinate[]
			{
				new Coordinate(envelope.MinX, envelope.MinY),
				new Coordinate(envelope.MaxX, envelope.MinY),
				new Coordinate(envelope.MaxX, envelope.MaxY),
				new Coordinate(envelope.MinX, envelope.MaxY),
				new Coordinate(envelope.MinX, envelope.MinY)
			});
			return base.CreatePolygon(shell, null);
		}

		public override IPolygon CreatePolygon(Coordinate[] coordinates)
		{
			ILinearRing shell = this.CreateLinearRing(coordinates, true);
			return base.CreatePolygon(shell);
		}

		public override IPolygon CreatePolygon(ICoordinateSequence coordinates)
		{
			ILinearRing shell = this.CreateLinearRing(coordinates, true);
			return base.CreatePolygon(shell);
		}

		public override IPolygon CreatePolygon(ILinearRing shell)
		{
			return this.CreatePolygon(shell, null);
		}

		public override IPolygon CreatePolygon(ILinearRing shell, ILinearRing[] holes)
		{
			if (shell != null && !shell.IsCCW)
			{
				shell = OgcCompliantGeometryFactory.ReverseRing(shell);
			}
			if (holes != null)
			{
				for (int i = 0; i < holes.Length; i++)
				{
					if (holes[i].IsCCW)
					{
						holes[i] = OgcCompliantGeometryFactory.ReverseRing(holes[i]);
					}
				}
			}
			return base.CreatePolygon(shell, holes);
		}
	}
}
